//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.eResearch.Ontology.Mapping.Mappers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Workflow.Runtime.Tracking;
    using Microsoft.Research.eResearch.Ontology.Lookup;
    using System.Xml.Serialization;
    using System.IO;
    using System.Xml;
    using Microsoft.Research.eResearch.Blackboard.Common;
    using System.Diagnostics;
    using System.Globalization;

    /// <summary>
    /// Implementation for ActivityTrackingRecord mapping mediator.
    /// </summary>
    [OntologicalTypeMapper(typeof(ActivityTrackingRecord))]
    internal class ActivityTrackingRecordMapper : IOntologicalTypeMapper
    {
        /// <summary>
        /// Creates list of all the concept value pairs for the given object using lookup table.
        /// </summary>
        /// <param name="table">Instance of the lookup table.</param>
        /// <param name="input">Instance of the input object.</param>
        /// <returns>List of concept value pairs.</returns>
        public List<List<ConceptValuePair>> MapConceptValuePairs(IOntologyLookupTable table, object input)
        {
            List<List<ConceptValuePair>> messages = new List<List<ConceptValuePair>>();

            ActivityTrackingRecord atr = input as ActivityTrackingRecord;
            if (atr != null)
            {
                List<ConceptValuePair> message = CreateMessage(table, atr);
                messages.Add(message);

                foreach (TrackingDataItem item in atr.Body)
                {
                    message = CreateMessage(table, atr);
                    if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(TrackingDataItem), "Data")))
                    {
                        // Extract out the property values
                        if (null != item.Data)
                            message.Add(new ConceptValuePair(
                                table.MapPropertyToConcept(typeof(TrackingDataItem), "Data"),
                                PackDataValue(item.Data)
                                ));
                        else
                            message.Add(new ConceptValuePair(
                                table.MapPropertyToConcept(typeof(TrackingDataItem), "Data"),
                                String.Format(CultureInfo.CurrentUICulture, "{0}", item.Data)));
                    }

                    if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(TrackingDataItem), "FieldName")))
                    {
                        message.Add(new ConceptValuePair(
                            table.MapPropertyToConcept(typeof(TrackingDataItem), "FieldName"),
                            item.FieldName));
                    }

                    if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(TrackingDataItem), "Annotations")))
                    {
                        message.Add(new ConceptValuePair(
                            table.MapPropertyToConcept(typeof(TrackingDataItem), "Annotations"),
                            item.Annotations[0]));
                    }

                    if (BlackBoardSettings.Concepts.IsConceptPresent(
                                            table.MapPropertyToConcept(typeof(TrackingDataItem), "DataType")))
                    {
                        message.Add(new ConceptValuePair(
                            table.MapPropertyToConcept(typeof(TrackingDataItem), "DataType"),
                            String.Format(CultureInfo.CurrentUICulture, "{0}", item.Annotations[1])));
                    }

                    messages.Add(message);
                }
            }
            else
            {
                // Something is wrong with the OntologicalTypeMapperManager
                throw new NotSupportedException("There is a problem with the OntologicalTypeMapperManager.");
            }

            return messages;
        }

        /// <summary>
        /// Performs basic lookup and mapping for activity tracking record.
        /// </summary>
        /// <param name="table">Instance of the lookup table.</param>
        /// <param name="atr">Instance of the activity tracking record.</param>
        /// <returns>List of concept value pairs.</returns>
        private static List<ConceptValuePair> CreateMessage(IOntologyLookupTable table, ActivityTrackingRecord atr)
        {
            List<ConceptValuePair> message = new List<ConceptValuePair>();

            // Properties from TrackingRecord
            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "EventDateTime")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "EventDateTime"),
                    atr.EventDateTime.ToUniversalTime().Ticks.ToString()));
            }

            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "EventOrder")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "EventOrder"),
                    atr.EventOrder.ToString(CultureInfo.CurrentCulture)));
            }

            // Properties from ActivityTrackingRecord
            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ActivityType")))
            {
                message.Add(new ConceptValuePair(
                   table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ActivityType"),
                   atr.ActivityType.ToString()));
            }

            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ContextGuid")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ContextGuid"),
                    atr.ContextGuid.ToString()));
            }

            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ExecutionStatus")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ExecutionStatus"),
                    atr.ExecutionStatus.ToString()));
            }

            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ParentContextGuid")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "ParentContextGuid"),
                    atr.ParentContextGuid.ToString()));
            }

            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "QualifiedName")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "QualifiedName"),
                    atr.QualifiedName));
            }

            if (BlackBoardSettings.Concepts.IsConceptPresent(
                        table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "Annotations")))
            {
                message.Add(new ConceptValuePair(
                    table.MapPropertyToConcept(typeof(ActivityTrackingRecord), "Annotations"),
                    atr.Annotations.Count == 1 ? atr.Annotations[0] : atr.QualifiedName));
            }

            return message;
        }

        /// <summary>
        /// This method access the static policy information and then decides how to 
        /// pack the values depending on the type. This data value is ultimately a 
        /// string but depending on the policy the value could either be a simple ToString
        /// representation of the object or a serialized representation.
        /// </summary>
        private string PackDataValue(object data)
        {
            string dataValue = "";

            // Get the serializtion method to be used for the current type
            SerializtionMethodTypes serType = BlackBoardSettings.SerializationSettings.GetSerializationMethod(data.GetType());
            if (SerializtionMethodTypes.XmlSerialize == serType)
            {
                try
                {
                    XmlSerializer serializer = new XmlSerializer(data.GetType());
                    using (MemoryStream stream = new MemoryStream())
                    {
                        XmlWriter wtr = XmlWriter.Create(stream);

                        serializer.Serialize(wtr, data);

                        byte[] contents = new byte[stream.Length];
                        stream.Position = 0;
                        stream.Read(contents, 0, (int)stream.Length);

                        System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
                        dataValue = enc.GetString(contents);
                    }
                }
                catch (InvalidOperationException)
                {
                    dataValue = data.ToString();
                }
                catch (XmlException)
                {
                    dataValue = data.ToString();
                }
                catch (Exception)
                {
                    dataValue = data.ToString();
                }
            }
            else if (SerializtionMethodTypes.ToString == serType)
                dataValue = data.ToString();

            return dataValue;
        }

        /// <summary>
        /// Looks up the list to find if the type is to be serialized using the
        /// XML serilizer
        /// </summary>
        /// <param name="objType"></param>
        /// <returns></returns>
        private bool IsTypeSerializable(Type objType)
        {
            if (null == serializableTypes)
                LoadSerializableTypeList();

            string serialType = serializableTypes.Find(delegate(string type) { return type == objType.ToString(); });
            return !(serialType == null);
        }

        /// <summary>
        /// This method reads the types from the config file which are
        /// to be serialized.
        /// </summary>
        private void LoadSerializableTypeList()
        {
        }

        /// <summary>
        /// List of types read from the config file which are to be serialized
        /// when packing the value.
        /// </summary>
        private static List<string> serializableTypes = null;
    }
}
